﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Win32;
using System.Globalization;
using System.Runtime.InteropServices;
using MefTZAddin.interop.types;
using MefTZAddin.interop;

namespace MefTZAddin
{
    public class TimeZoneInterop
    {
        public static void SetTimeZone(TimeZoneInfo tzi)
        {
            SetTimeZone(ConvertToTimeZoneInformation(tzi));
        }

        /// <summary>
        /// Sets new time-zone information for the local system.
        /// </summary>
        /// <param name="tzi">Struct containing the time-zone parameters to set.</param>
        public static void SetTimeZone(TimeZoneInformation tzi)
        {
            // Setting timezone is filtered from token by default
            AdjustPrivileges.SetTokenPrivilege(AdjustPrivileges.SE_TIME_ZONE_NAME);
            Win32.CheckWin32Error();

            // Set local system timezone
            if (SetTimeZoneInformation(ref tzi))
            {
                Win32.CheckWin32Error();
            }
            else
            {
                CultureInfo.CurrentCulture.ClearCachedData();
                TimeZoneInfo.ClearCachedData();
                SendMessageInterop.BroadcastSettingsChange();
            }

            // Setting timezone is filtered from token by default
            AdjustPrivileges.UnsetTokenPrivilege(AdjustPrivileges.SE_TIME_ZONE_NAME);
            Win32.CheckWin32Error();
        }

        public static TimeZoneInformation ConvertToTimeZoneInformation(TimeZoneInfo tzi)
        {
            TimeZoneInformation cpy = new TimeZoneInformation();
            cpy.bias = (int)tzi.BaseUtcOffset.TotalMinutes;

            TimeZoneInfo.AdjustmentRule[] rules = tzi.GetAdjustmentRules();

            if (tzi.SupportsDaylightSavingTime && rules.Length > 0)
            {
                cpy.daylightName = tzi.DaylightName;
                cpy.daylightBias = (int)rules[0].DaylightDelta.TotalMinutes;

                foreach (var rule in rules)
                {
                    if (DateTime.Now >= rule.DateStart && DateTime.Now <= rule.DateEnd)
                    {
                        cpy.daylightDate = rule.DaylightTransitionStart.ToSystemTime();
                        cpy.standardDate = rule.DaylightTransitionEnd.ToSystemTime();
                        break;
                    }
                }
                cpy.daylightDate = rules[0].DateStart.ToSystemTime();
            }
            else cpy.daylightName = "";

            cpy.standardName = tzi.StandardName;

            return cpy;
        }

        /// <summary>
        /// Gets current timezone information for the local system.
        /// </summary>
        /// <returns>Struct containing the current time-zone parameters.</returns>
        public static TimeZoneInformation GetTimeZone()
        {
            // create struct instance
            TimeZoneInformation tzi;

            // retrieve timezone info
            int currentTimeZone = GetTimeZoneInformation(out tzi);

            return tzi;
        }


        /// <summary>
        /// [Win32 API call]
        /// The GetTimeZoneInformation function retrieves the current time-zone parameters.
        /// These parameters control the translations between Coordinated Universal Time (UTC)
        /// and local time.
        /// </summary>
        /// <param name="lpTimeZoneInformation">[out] Pointer to a TIME_ZONE_INFORMATION structure to receive the current time-zone parameters.</param>
        /// <returns>
        /// If the function succeeds, the return value is one of the following values.
        /// <list type="table">
        /// <listheader>
        /// <term>Return code/value</term>
        /// <description>Description</description>
        /// </listheader>
        /// <item>
        /// <term>TIME_ZONE_ID_UNKNOWN == 0</term>
        /// <description>
        /// The system cannot determine the current time zone. This error is also returned if you call the SetTimeZoneInformation function and supply the bias values but no transition dates.
        /// This value is returned if daylight saving time is not used in the current time zone, because there are no transition dates.
        /// </description>
        /// </item>
        /// <item>
        /// <term>TIME_ZONE_ID_STANDARD == 1</term>
        /// <description>
        /// The system is operating in the range covered by the StandardDate member of the TIME_ZONE_INFORMATION structure.
        /// </description>
        /// </item>
        /// <item>
        /// <term>TIME_ZONE_ID_DAYLIGHT == 2</term>
        /// <description>
        /// The system is operating in the range covered by the DaylightDate member of the TIME_ZONE_INFORMATION structure.
        /// </description>
        /// </item>
        /// </list>
        /// If the function fails, the return value is TIME_ZONE_ID_INVALID. To get extended error information, call GetLastError.
        /// </returns>
        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        private static extern int GetTimeZoneInformation(out TimeZoneInformation lpTimeZoneInformation);

        /// <summary>
        /// [Win32 API call]
        /// The SetTimeZoneInformation function sets the current time-zone parameters.
        /// These parameters control translations from Coordinated Universal Time (UTC)
        /// to local time.
        /// </summary>
        /// <param name="lpTimeZoneInformation">[in] Pointer to a TIME_ZONE_INFORMATION structure that contains the time-zone parameters to set.</param>
        /// <returns>
        /// If the function succeeds, the return value is nonzero.
        /// If the function fails, the return value is zero. To get extended error information, call GetLastError.
        /// </returns>
        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        private static extern bool SetTimeZoneInformation([In] ref TimeZoneInformation lpTimeZoneInformation);


    }
}
